from khronos import Atomic, Request, chain, INF
from khronos.support import Deque

class Level(Atomic):
    def __init__(self, capacity=INF, initial_level=0.0, 
                 name=None, parent=None, priority=None):
        Atomic.__init__(self, name, parent, priority)
        self.__capacity = capacity
        self.__level = initial_level
        self.__initial_level = initial_level
        self.__getreqs = Deque()
        self.__putreqs = Deque()
        
    def status(self):
        return "level=%f of %f (requests: get=%d / put=%d)" % \
            (self.__level, self.__capacity, len(self.__getreqs), len(self.__putreqs))
             
    def initializer(self):
        if self.__initial_level > self.__capacity:
            raise ValueError("initial level exceeds capacity")
        self.__level = self.__initial_level
        self.__getreqs.clear()
        self.__putreqs.clear()
        
    @chain
    def put(self, qty):
        if qty > self.__capacity:
            raise ValueError("unable to put - quantity exceeds Level capacity")
            
        req = Request(qty, 
                      deploy=self.__putreqs.append, 
                      retract=self.__putreqs.remove)
        if self.__level + qty <= self.__capacity:
            req.satisfy()
        
        yield req
        self.__level += qty
        # after adding the amount to the level, check for satisfiable getreqs
        while len(self.__getreqs) > 0 and self.__getreqs[0].data <= self.__level:
            self.__getreqs[0].satisfy()
            
    @chain
    def get(self, qty):
        if qty > self.__capacity:
            raise ValueError("unable to get - quantity exceeds Level capacity")
            
        req = Request(qty, 
                      deploy=self.__getreqs.append, 
                      retract=self.__getreqs.remove)
        if self.__level >= qty:
            req.satisfy()
            
        yield req
        self.__level -= qty
        # after getting, see if we can satisfy any put requests
        while len(self.__putreqs) > 0 and self.__putreqs[0].data + self.__level <= self.__capacity:
            self.__putreqs[0].satisfy()
            
    def set_capacity(self, capacity):
        if self.initialized():
            if capacity < self.__level:
                raise ValueError("unabled to reduce capacity to %f - less than current level (%f)"
                                 % (capacity, self.__level))
            delta = capacity - self.__capacity
            self.__capacity = capacity
            if delta > 0:
                self.execute(self.put(delta))
            else:
                self.__level += delta
        else:
            self.__capacity = capacity
            
    def get_capacity(self):
        return self.__capacity
        
    def get_level(self):
        return self.__level
        
    def get_free(self):
        return self.__capacity - self.__level
        
        